
import threading

import wx.lib.newevent

# it create a new event class and a EVT binder function
(UpdateSubInfoEvent, EVT_UPDATE_SUBINFO) = wx.lib.newevent.NewEvent()

(UpdateGaugeEvent, EVT_UPDATE_GAUGEGRAPH) = wx.lib.newevent.NewEvent()


class LoadGaugeThread:
    def __init__(self, win, text, callback, extra=False, *args, **kws):
        self.win = win
        self.cb = callback
        self.extra = extra
        self.args = args
        self.kws = kws
        self.lastv = -1
        self.keepGoing = False
        self.isStopped = False

    def start(self):
        self.keepGoing = True
        if self.extra:
            self.thread = threading.Thread(target=self.runEx, args=())
        else:
            self.thread = threading.Thread(target=self.run, args=())

        self.thread.start()

    def stop(self):
        self.keepGoing = False

    def run(self):
        while self.keepGoing:
            val, text = self.cb(self.notify)

            try:
                event = UpdateGaugeEvent(value=int(val), text=text)
                wx.PostEvent(self.win, event)
            except ValueError:
                pass

            if val == 100 or val == -1:
                break

        self.isStopped = True

    def runEx(self):
        self.cb(self.notify, *self.args, **self.kws)

        self.notify(100)
        self.isStopped = True

    def stopped(self):
        return self.isStopped

    def notify(self, *args, **kws):
        val = 0
        text = ''

        if len(args) > 0:
            val = args[0]
        if len(args) > 1:
            text = args[1]

        if val == self.lastv:
            return
        else:
            self.lastv = val

        self.win.app.debug('PROCESS [%d] %s' % (val, text))
        event = UpdateGaugeEvent(value=val, text=text)
        wx.PostEvent(self.win, event)

    def UpdateSubInfo(self, inf):
        evt = UpdateSubInfoEvent(info=inf)
        wx.PostEvent(self.win, evt)


class ProgressFrame(wx.Dialog):
    def __init__(self, parent, app, title, run, extra=False, *args, **kws):
        wx.Dialog.__init__(self, parent, -1, title,
                           wx.Point(300, 300), wx.Size(500, 120))

        self.str = ''
        self.info = ''

        self.app = app
        self.panel = wx.Panel(self, -1)
        self.text = wx.StaticText(self.panel, -1, '', wx.Point(45, 15))
        self.subinfo = wx.StaticText(self.panel, -1, '', wx.Point(60, 15))

        # create graphic window
        self.step = 0
        self.text.SetLabel(title)
        self.gauge = wx.Gauge(self.panel, -1, 100, wx.Point(30, 50),
                              wx.Size(450, 20),
                              wx.GA_HORIZONTAL | wx.GA_SMOOTH)
        self.gauge.SetBezelFace(5)
        self.gauge.SetShadowWidth(5)
        # bind update message
        self.Bind(EVT_UPDATE_GAUGEGRAPH, self.OnUpdateGaugegraph)
        # bind sub-info message
        self.Bind(EVT_UPDATE_SUBINFO, self.OnUpdateSubInfo)
        # initialize the gauge thread

        self.gaugeThread = LoadGaugeThread(self, title, run,
                                           extra, *args, **kws)
        self.gaugeThread.start()

    def OnUpdateGaugegraph(self, event):
        val = event.value
        if val == 100 or val == -1:
            self.Close()
        else:
            if val > 100:
                val = 100
            self.gauge.SetValue(event.value)

        if self.str != event.text and event.text is not None:
            self.str = event.text
            self.text.SetLabel(self.str)

    def OnUpdateSubInfo(self, event):
        if self.info != event.info:
            self.info = event.info
            self.subinfo.SetLabel(self.info)

    def Close(self):
        self.gaugeThread.stop()
        while not self.gaugeThread.stopped():
            pass

        self.Refresh()
        if self.IsModal():
            self.EndModal(wx.ID_OK)
        self.Destroy()


def ExecuteWithBackgroundThread(threshold, parent, name,
                                callback, *args, **kws):
    if not threshold:
        callback(*args, **kws)
    else:
        fr = ProgressFrame(parent, -1, name, callback, True, *args, **kws)
        fr.CenterOnScreen(wx.BOTH)
        fr.SetWindowStyleFlag(wx.RESIZE_BORDER |
                              wx.RESIZE_BOX | wx.MAXIMIZE_BOX)
        fr.ShowModal()
        fr.Close()
